iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0
Kubernetes

不是背 YAML!30 天從零打造 Kubernetes 微服務:從本機實戰一路到 CKA系列 第 18

Day 18|Node 要維修怎麼辦?cordon、drain、uncordon 與 PDB 實戰

  • 分享至 

  • xImage
  •  

前面我們一直在操作 Pod、Deployment、Service,但別忘了:

Pod 最後一定是跑在某一台 Node 上。

例如現在 Cluster 有三台 Worker:

worker1
worker2
worker3

API 有 3 個 replicas,Scheduler 可能把它們分配成:

worker1
└── API Pod 1

worker2
└── API Pod 2

worker3
└── API Pod 3

但 Production 的 Server 不可能永遠不用維護。

例如我們可能需要:

OS Patch
Kernel Update
Kubernetes Upgrade
硬體維修
更換 VM / Instance

假設今天要維護:

worker2

問題就來了。

上面還有:

API Pod 2

總不能直接把 Server 關掉。

所以 Kubernetes 的想法是:

先停止把新的工作派過來,再把原本能搬走的工作移走,最後才維護 Node。

流程就是:

確認 Node 上有什麼
        ↓
cordon
        ↓
drain
        ↓
maintenance
        ↓
uncordon

📝 cordon, drain 是什麼?

這邊先給的定義,先有個粗步概念即可,下面會再一一解釋清楚!

cordon:

用來將 Node 標記成不可調度 (SchedulingDisabled)。阻止任何新的 Pod 被調度(schedule)到該節點上。

  • 影響:節點上原本已經在運行的 Pod 不受任何影響,會繼續正常運作。
  • 指令kubectl cordon <node-name>
  • 解除:維護完成後使用 kubectl uncordon <node-name> 恢復調度。

drain:

用來將 Node 標記成不可調度 (SchedulingDisabled)。用於安全驅逐 (evict) 節點上的 Pod。本質上就是「先標記 cordon 避免新負載搶入,再透過 Eviction API 安全逐出舊 Pod」。

  • 常見情境:準備進行硬體更換、重開機或升級。
  • 作用:底層會先自動執行 cordon,接著逐一安全驅逐該節點上所有的 Pod,讓控制器(如 Deployment、StatefulSet)在其他健康的節點上重建這些 Pod。
  • 影響:節點上的業務 Pod 會被遷移;若該節點包含 DaemonSet 或沒有控制器託管的獨立 Pod,需加上特定參數才能順利執行。
  • 常用指令
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data

** cordon, drain 比較表**

動作 是否標記不可調度? 是否驅逐現有 Pod? 適用情境
cordon 觀察節點狀態、暫停負載流入,但暫不中斷現有服務
drain 節點需維修、更換硬體、重開機或做節點系統升級

標準維修流程通常為:kubectl drain <node> $\rightarrow$ 進行節點維護 $\rightarrow$ kubectl uncordon <node>


接下來就要實際進行實作啦

再說明一次前面提到的情境
今天我們要維護 Worker2,但他上面還有pod在運行
又不能直接關掉,所以我們現在就是必須做兩件事情:

  1. 停止再接收新工作進來 (因為我都要搬家啦)<-> cordon
  2. 把原本的工作移走 (安全驅逐既有的) <-> drain
  3. 再進行 Node 維護

流程就是:

確認 Node 上有什麼
        ↓
cordon
        ↓
drain
        ↓
maintenance
        ↓
uncordon

第一步:先確認這台 Node 上跑了什麼

維護之前先看:

先看一下 NodeName

kubectl get nodes

https://ithelp.ithome.com.tw/upload/images/20260918/20168537S3XHQ7mJhq.png

kubectl get pods \
  -A \
  --field-selector spec.nodeName=cka-lab-worker2 \
  -o wide

會看到:
https://ithelp.ithome.com.tw/upload/images/20260918/20168537U90j1jd3b2.png

為什麼不能直接 drain?

因為這些 Pod 不一定都是同一種類型。

例如:

API Pod

通常是 Deployment 管理的,刪掉之後可以在其他 Node 重建。

但是 PostgreSQL 如果使用 Local PersistentVolume:

PostgreSQL Pod
        ↓
資料綁在 worker2 的硬碟

那就不能很單純地說:

搬去 worker1 就好

因為資料可能根本不在 worker1。

所以維護 Node 前第一個觀念是:

先確認哪些東西可以搬,哪些東西跟這台 Node 有特殊關係。


cordon:先把入口關起來

假設確定今天要維護:

cka-lab-worker2

先執行:

kubectl cordon cka-lab-worker2

這時:

kubectl get nodes

會看到:

cka-lab-worker2   Ready,SchedulingDisabled

這個 SchedulingDisabled 很重要。

它代表:

Scheduler 暫時不要再把新的 Pod 放到這台 Node。

但是原本上面的 Pod:

API Pod
Redis Pod

還是繼續跑。

所以 cordon 可以想成:

旅館要準備整修

先在門口掛:

今天不再接受新客人

但是:

原本住在裡面的客人

還沒有被趕出去。

所以:

cordon = 不再接新的 Pod

不是:

cordon = 把 Pod 刪掉

drain:把原本的 Pod 搬出去

接下來才執行:

kubectl drain \
  cka-lab-worker2 \
  --ignore-daemonsets \
  --delete-emptydir-data

drain 可以理解成:

準備把這台 Node 清空,讓它可以安全維護。

假設原本:

worker2
└── API Pod 2

API Pod 2 是 Deployment 管理的。

drain 之後:

API Pod 2
↓
被 Evict

這裡的 Evict 可以先簡單理解成:

請這個 Pod 正常離開這台 Node。

Pod 消失後,Deployment 會發現:

我明明設定 replicas: 3

現在怎麼只剩 2 個?

所以 Deployment 會自動建立新的 Pod。

Scheduler 再幫它找新的 Node:

worker1
或
worker3

最後可能變成:

worker1
├── API Pod 1
└── API Pod 4

worker3
└── API Pod 3

因此真正負責「補 Pod」的不是 drain。

而是:

Deployment / ReplicaSet

drain 只是把 Pod 請離這台 Node。


那 DaemonSet 到底是什麼?

https://ithelp.ithome.com.tw/upload/images/20260918/20168537GfIbiPY4TV.png

這裡最容易卡住。

Deployment 的概念通常是:

我要整個 Cluster 總共有幾個 Pod。

例如:

replicas: 3

代表:

整個 Cluster 有 3 個 API Pod

至於它們在哪些 Node,由 Scheduler 決定。

但是 DaemonSet 完全不同。

DaemonSet 的想法是:

每一台 Node 都應該跑一份這種 Pod。

最典型的例子就是:

Log Agent
Monitoring Agent
Node-level Network Agent

例如有三台 Node:

worker1
worker2
worker3

DaemonSet 可能變成:

worker1
└── Log Agent

worker2
└── Log Agent

worker3
└── Log Agent

不是因為你設定:

replicas: 3

而是因為:

有 3 台符合條件的 Node

所以 Kubernetes 自動讓每台都有一個。


為什麼 drain 要加 --ignore-daemonsets

現在假設我們 drain:

worker2

上面有:

API Pod
Log Agent

API Pod 可以搬走。

但是 Log Agent 是 DaemonSet 管理的。

DaemonSet 本來的規則就是:

worker2 存在,就應該有一個 Log Agent。

所以如果 drain 把它當普通 Pod 刪掉:

Log Agent 被刪掉

DaemonSet Controller 馬上會想:

等等。

worker2 上怎麼沒有 Log Agent?

然後又建立一個。

變成:

刪除
↓
建立
↓
刪除
↓
建立

這顯然沒有意義。

所以 kubectl drain 看到 DaemonSet Pod 時,通常會要求你明確告訴它:

--ignore-daemonsets

意思不是:

把 DaemonSet 刪掉。

而是:

這些 DaemonSet Pod 不用搬,我知道它們存在,繼續處理其他 Pod 就好。

所以可以記:

Deployment Pod
→ drain 會嘗試 Evict

DaemonSet Pod
→ drain 不把它當普通 Workload 搬家

emptyDir 又是什麼?

有些 Pod 需要一小塊暫存空間。

例如:

下載暫存檔
Cache
中間處理結果

這時可以使用:

emptyDir

例如:

Pod
└── emptyDir
    └── temp.txt

問題是:

emptyDir 的資料是跟這個 Pod 綁在一起的。

當 Pod 被刪掉:

Pod 消失
↓
emptyDir 資料也消失

所以 drain 如果發現:

這個 Pod 有 emptyDir

它會提醒你:

如果我把 Pod 移走
這些資料會不見喔

如果你確定只是 Cache 或 Temporary Data,可以使用:

--delete-emptydir-data

意思就是:

我知道這些暫存資料會消失,可以接受。

但如果裡面是不能丟的資料,就不能亂加。


所以 drain 整體到底做了什麼?

現在把整件事重新看一次。

原本:

worker2
├── API Pod
├── Worker Pod
└── Log Agent(DaemonSet)

先 cordon:

worker2
SchedulingDisabled

新的 Pod 不會再進來。

接著 drain:

API Pod
↓
Evict

Worker Pod
↓
Evict

Log Agent
↓
ignore DaemonSet

Deployment 會重新補:

API Pod
Worker Pod

到其他 Node。

最後可能變成:

worker2
└── Log Agent

這時主要 Application Workload 已經離開了,就可以開始維護 Node。


維護完成後:uncordon

維護完成:

kubectl uncordon cka-lab-worker2

意思就是:

這台 Node 恢復營業,可以重新接受新的 Pod。

所以最重要的三個指令可以這樣記:

cordon
= 不要再派新的 Pod 進來

drain
= 把原本可以移動的 Pod 搬出去

uncordon
= 重新允許 Pod 進來

那 PodDisruptionBudget (PDB) 又是在解決什麼?

現在假設 API 有:

3 replicas

分布是:

worker1
└── API 1

worker2
├── API 2
└── API 3

今天我們 drain:

worker2

如果 API 2 和 API 3 同時被移走:

3 個 API
↓
瞬間剩 1 個

服務可能突然扛不住。

所以我們可以設定:

apiVersion: policy/v1
kind: PodDisruptionBudget

metadata:
  name: api-pdb
  namespace: cka-lab

spec:
  minAvailable: 2

  selector:
    matchLabels:
      app: api

意思是:

當 Kubernetes 在做可以控制的中斷時,至少要保留 2 個 Available API Pod。

所以如果現在:

API 1 Ready
API 2 Ready
API 3 Ready

可以先 Evict 一個。

變成:

2 個 Available

這時再想 Evict 第二個,PDB (PodDisruptionBudget) 就可能阻止:

等等。

再刪下去就只剩 1 個了。

等新的 API Pod 在其他 Node Ready:

Available 回到 3

才繼續下一個 Eviction。


PDB (PodDisruptionBudget) 不是防止 Server 壞掉

這裡也要特別注意。

PDB 可以影響的主要是:

kubectl drain

這種 Kubernetes 知道、也有機會控制的中斷。

但是如果:

worker2 突然斷電

Kubernetes 根本沒有時間說:

等等,我 PDB 要 minAvailable: 2

Server 已經直接消失了。

所以:

PDB

比較像:

我們「主動維護」時,不要一次關掉太多服務。

而真正的 High Availability 還是要靠:

多 replicas
+
Pod 分散到不同 Node
+
健康檢查 (health check)
+
Anti-Affinity

Day 18 最重要的觀念

今天不用背很多東西,只要建立這個畫面:

今天要維護 worker2
        ↓
先看看上面有什麼
        ↓
cordon
不准新的 Pod 進來
        ↓
drain
把 Deployment 等 Workload 搬走
        ↓
DaemonSet 不當一般 Pod 搬
        ↓
維護 Server
        ↓
uncordon
重新開放

而:

PodDisruptionBudget

就是在 drain 這種「可控制的中斷」裡,多加一道規則:

搬 Pod 可以,但不要一次把我的服務搬到沒人可以接 Request。

如果你只記一句話,可以記成:

cordon 是封路 (不再收人)
drain 是搬家 (舊住戶搬移)
DaemonSet 是每台 Node 都要有的駐點人員
PDB 是規定搬家時至少要留幾個人繼續上班

上一篇
Day 17|Affinity 與 Anti-Affinity:不只是「能不能排」,還要考慮「排得好不好」
系列文
不是背 YAML!30 天從零打造 Kubernetes 微服務:從本機實戰一路到 CKA18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言